﻿using FastCloud.Base.Config;
using FastUtil.Config;
using FastUtil.Data;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;

namespace FastCloud.Base.Config
{
    public class PluginManage
    {
        private static DirectoryInfo PluginFolder = new DirectoryInfo(CloudUtil.GetContentPath()+"/Plugin");
        private static DirectoryInfo TempPluginFolder = new DirectoryInfo(CloudUtil.GetContentPath() + "/PluginTemplate");
        private static IDictionary<string, Type> ControllerTypeDic = new Dictionary<string, Type>();
        private static List<Assembly> DllList = new List<Assembly>();
        private static readonly FileSystemWatcher _FileSystemWatcher = new FileSystemWatcher();
        private static void ScanPlugin()
        {
            if (!PluginFolder.Exists)
            {
                PluginFolder.Create();
            }
            if (!TempPluginFolder.Exists)
            {
                TempPluginFolder.Create();
            }

            TempPluginFolder.Attributes = FileAttributes.Normal & FileAttributes.Directory;
            PluginFolder.Attributes = FileAttributes.Normal & FileAttributes.Directory;
            //清理临时文件。
            foreach (var file in TempPluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))
            {
                try
                {
                    File.SetAttributes(file.FullName, FileAttributes.Normal);
                    file.Delete();
                }
                catch (Exception e)
                {
                    throw new Exception("请检查IIS权限");
                }

            }
            //复制插件进临时文件夹。
            foreach (var plugin in PluginFolder.GetFiles("*.dll", SearchOption.AllDirectories))
            {
                try
                {
                    string CopyFilePath = Path.Combine(TempPluginFolder.FullName, plugin.Name);
                    File.Copy(plugin.FullName, CopyFilePath, true);
                    File.SetAttributes(CopyFilePath, FileAttributes.Normal);
                }
                catch (Exception e)
                {
                    throw new Exception("请检查IIS权限");
                }
            }
        }

        public static void LoadPlugin()
        {
            ScanPlugin();
            IEnumerable<Assembly> AssemblyList = GetPluginAssemblies();
            foreach (Assembly assembly in AssemblyList)
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (type.IsAssignableFrom(typeof(Controller)) && type.Name.Contains("Controller") && type.IsClass && !type.IsAbstract)
                    {
                        string Name = type.Name;
                        //如果有自定义的路由注解
                        if (type.IsDefined(typeof(Route), false))
                        {
                            var areaattribute = type.GetCustomAttributes(typeof(Route), false).FirstOrDefault();
                            Name = ((Route)areaattribute).Name;
                            Name += "Controller";

                        }
                        if (!ControllerTypeDic.ContainsKey(Name))
                        {
                            ControllerTypeDic.Add(Name, type);
                        }
                    }
                }
                var controllerAssemblyPart = new AssemblyPart(assembly);
                CloudUtil.GetBuilder().ConfigureApplicationPartManager(apm =>
                {
                    apm.ApplicationParts.Add(controllerAssemblyPart);
                });
            }
            CloudUtil.GetBuilder().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
            StartPluginRefreshWatch();
        }

        public static IEnumerable<Assembly> GetPluginAssemblies()
        {
            DllList.Clear();
            IEnumerable<Assembly> assemblies = Directory.EnumerateFiles(TempPluginFolder.FullName, "*.dll").Select(AssemblyLoadContext.Default.LoadFromAssemblyPath);
            foreach (Assembly item in assemblies)
            {
                DllList.Add(item);
            }
            return DllList;
        }

        public static List<Assembly> GetDillList()
        {
            return DllList;
        }

        public static Type GetControllerType(string ControllerName)
        {
            if (ControllerTypeDic.ContainsKey(ControllerName))
            {
                return ControllerTypeDic[ControllerName];
            }

            return null;
        }

        private static void StartPluginRefreshWatch()
        {
            _FileSystemWatcher.Path = CloudUtil.GetContentPath() + "/Plugin";
            _FileSystemWatcher.Filter = "*.dll";
            _FileSystemWatcher.Changed += _FileSystemWatcher_Changed;

            _FileSystemWatcher.IncludeSubdirectories = true;
            _FileSystemWatcher.NotifyFilter =
                NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;
            _FileSystemWatcher.EnableRaisingEvents = true;

            if (AppConfigUtil.Configuration["Frame:ViewsPublishType"]=="AutoCopy"
                && !string.IsNullOrEmpty(AppConfigUtil.Configuration["Frame:ViewsVirtualPath"]) 
                && Directory.Exists(AppConfigUtil.Configuration["Frame:ViewsVirtualPath"]))
            {
                FileSystemWatcher ViewsWatcher = new FileSystemWatcher();
                ViewsWatcher.Path = AppConfigUtil.Configuration["Frame:ViewsVirtualPath"];
                ViewsWatcher.Changed += ViewsWatcher_Changed;

                ViewsWatcher.IncludeSubdirectories = true;
                ViewsWatcher.NotifyFilter =
                    NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size;
                ViewsWatcher.EnableRaisingEvents = true;

                //系统启动时整个文件夹拷贝
                DirectoryInfo FrameWorkViews = new DirectoryInfo(CloudUtil.GetContentPath() + "/Views");
                DirectoryInfo ProjectViews = new DirectoryInfo(AppConfigUtil.Configuration["Frame:ViewsVirtualPath"]);
                foreach (DirectoryInfo FDir in FrameWorkViews.GetDirectories())
                {
                    foreach (DirectoryInfo PDir in ProjectViews.GetDirectories())
                    {
                        if (PDir.Name== FDir.Name)
                        {
                            FileUtil.DeleteDirectoryContent(FDir.FullName);
                        }
                    }
                }
                FileUtil.CopyDirectory(AppConfigUtil.Configuration["Frame:ViewsVirtualPath"],CloudUtil.GetContentPath() + "/Views", true);
            }
        }

        private static void _FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            CloudUtil.Restrat();
        }

        private static void ViewsWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            string FileFolderPath = e.FullPath.Replace(e.FullPath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries)[e.FullPath.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries).Length-1], ""); ;
            string TargetSyncPath = CloudUtil.GetContentPath() + "/Views" + FileFolderPath.Replace(AppConfigUtil.Configuration["Frame:ViewsVirtualPath"], "");
            switch (e.ChangeType)
            {
                case WatcherChangeTypes.Created:
                    FileUtil.CopyDirectory(FileFolderPath, TargetSyncPath, true);
                    break;
                case WatcherChangeTypes.Deleted:
                    break;
                case WatcherChangeTypes.Changed:
                    FileUtil.CopyDirectory(FileFolderPath, TargetSyncPath, true);
                    break;
                case WatcherChangeTypes.Renamed:
                    FileUtil.CopyDirectory(FileFolderPath, TargetSyncPath, true);
                    break;
                case WatcherChangeTypes.All:
                    FileUtil.CopyDirectory(FileFolderPath, TargetSyncPath, true);
                    break;
            }
        }
    }
}
